﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using PMS.Core.Utils;
using PMS.Core.Utils.Http;
using PMS.Data.Entities.Customers;
using PMS.Data.Entities.Localization;
using PMS.Services.Authentication;
using PMS.Services.EntityServices.Customers;
using PMS.Services.EntityServices.Localization;
using PMS.Services.SharedKernel;
using PMS.Services.Utils;
using PMS.Web.Framework.StartupCfgs.StartupSetups;
using System;
using System.Linq;

namespace PMS.Web.Framework
{
    /// <summary>
    /// Represents work context for web application
    /// </summary>
    public partial class WebWorkContext : IWorkContext
    {
        #region Fields

        private readonly IAuthenticationProvider _authenticationProvider;
        private readonly ICustomerService _customerService;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ILanguageService _languageService;
        private readonly IStoreContext _storeContext;
        private readonly IWebHelper _webHelper;

        private Customer _cachedCustomer;
        private Language _cachedLanguage;

        #endregion

        #region Ctor

        public WebWorkContext(IAuthenticationProvider authenticationProvider,
            ICustomerService customerService,
            IHttpContextAccessor httpContextAccessor,
            ILanguageService languageService,
            IStoreContext storeContext,
            IWebHelper webHelper)
        {
            _authenticationProvider = authenticationProvider;
            _customerService = customerService;
            _httpContextAccessor = httpContextAccessor;
            _languageService = languageService;
            _storeContext = storeContext;
            _webHelper = webHelper;
        }

        #endregion

        #region Utilities

        /// <summary>
        /// Get nop customer cookie
        /// </summary>
        /// <returns>String value of cookie</returns>
        protected virtual string GetCustomerCookie()
        {
            var cookieName = $"{CookieDefaultConfigs.Prefix}{CookieDefaultConfigs.CustomerCookie}";
            return _httpContextAccessor.HttpContext?.Request?.Cookies[cookieName];
        }

        /// <summary>
        /// Set nop customer cookie
        /// </summary>
        /// <param name="customerGuid">Guid of the customer</param>
        protected virtual void SetCustomerCookie(Guid customerGuid)
        {
            if (_httpContextAccessor.HttpContext?.Response == null)
                return;

            //delete current cookie value
            var cookieName = $"{CookieDefaultConfigs.Prefix}{CookieDefaultConfigs.CustomerCookie}";
            _httpContextAccessor.HttpContext.Response.Cookies.Delete(cookieName);

            //get date of cookie expiration
            var cookieExpires = CookieDefaultConfigs.CustomerCookieExpires;
            var cookieExpiresDate = DateTime.Now.AddHours(cookieExpires);

            //if passed guid is empty set cookie as expired
            if (customerGuid == Guid.Empty)
                cookieExpiresDate = DateTime.Now.AddMonths(-1);

            //set new cookie value
            var options = new CookieOptions
            {
                HttpOnly = true,
                Expires = cookieExpiresDate,
                Secure = _webHelper.IsCurrentConnectionSecured()
            };
            _httpContextAccessor.HttpContext.Response.Cookies.Append(cookieName, customerGuid.ToString(), options);
        }

        /// <summary>
        /// Get language from the requested page URL
        /// </summary>
        /// <returns>The found language</returns>
        protected virtual Language GetLanguageFromUrl()
        {
            if (_httpContextAccessor.HttpContext?.Request == null)
                return null;

            //whether the requsted URL is localized
            var path = _httpContextAccessor.HttpContext.Request.Path.Value;
            if (!path.IsLocalizedUrl(_httpContextAccessor.HttpContext.Request.PathBase, false, out var language))
                return null;

            return language;
        }

        /// <summary>
        /// Get language from the request
        /// </summary>
        /// <returns>The found language</returns>
        protected virtual Language GetLanguageFromRequest()
        {
            if (_httpContextAccessor.HttpContext?.Request == null)
                return null;

            //get request culture
            var requestCulture = _httpContextAccessor.HttpContext.Features.Get<IRequestCultureFeature>()?.RequestCulture;
            if (requestCulture == null)
                return null;

            //try to get language by culture name
            var requestLanguage = _languageService.GetAllLanguages().FirstOrDefault(language =>
                language.CultureCode.Equals(requestCulture.Culture.Name, StringComparison.InvariantCultureIgnoreCase));

            //check language availability
            if (requestLanguage == null || !requestLanguage.IsActive)
                return null;

            return requestLanguage;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the current customer
        /// </summary>
        public virtual Customer CurrentCustomer
        {
            get
            {
                //whether there is a cached value
                if (_cachedCustomer != null)
                    return _cachedCustomer;

                Customer customer = null;
                //check whether request is made by a background (schedule) task
                if (_httpContextAccessor.HttpContext == null ||
                    _httpContextAccessor.HttpContext.Request.Path.Equals(new PathString($"{DefaultRoutePath.ScheduleTaskPath}"), StringComparison.InvariantCultureIgnoreCase))
                {
                    //in this case return built-in customer record for background task
                    customer = _customerService.GetOrCreateBackgroundTaskUser();
                    return customer;
                }

                if (customer == null || customer.IsDeleted || !customer.IsActive)
                {
                    //DavidLee 取消CustomerGuidCookie保存读取，防止Cookie长时间不过期
                    //var customerCookie = GetCustomerCookie();
                    //if (!string.IsNullOrEmpty(customerCookie))
                    //{
                    //    if (Guid.TryParse(customerCookie, out var customerGuid))
                    //    {
                    //        //get customer from cookie (should not be registered)
                    //        var customerByCookie = _customerService.GetCustomerByGuid(customerGuid);
                    //        if (customerByCookie != null && !customerByCookie.IsDeleted && customerByCookie.IsActive && _customerService.IsRegistered(customerByCookie))
                    //            customer = customerByCookie;
                    //        else customer = null;
                    //    }
                    //    else customer = null;
                    //}
                    //else customer = null;

                    //try to get registered user
                    //if(customer == null)
                    //{
                        customer = _authenticationProvider.AuthServcice.GetAuthenticatedCustomer();
                    //}
                }
                if(customer != null)
                {
                    if (!customer.IsDeleted && customer.IsActive && _customerService.IsRegistered(customer))
                    {
                        //SetCustomerCookie(customer.CustomerGuid);
                        //cache the found customer
                        _cachedCustomer = customer;
                    }
                }

                return _cachedCustomer;
            }
            set
            {
                //Null Reference Compatiable
                //set CurrentCustomer=null when SignOut Current Customer
                //DavidLee 取消CustomerGuidCookie保存读取，防止Cookie长时间不过期
                //SetCustomerCookie(value?.CustomerGuid ?? Guid.Empty);
                _cachedCustomer = value;
            }
        }

        /// <summary>
        /// Gets or sets current user working language
        /// </summary>
        public virtual Language WorkingLanguage
        {
            get
            {
                //whether there is a cached value
                if (_cachedLanguage != null)
                    return _cachedLanguage;

                Language detectedLanguage = GetLanguageFromUrl();

                //whether we should detect the language from the request
                if (detectedLanguage == null)
                    detectedLanguage = GetLanguageFromRequest();

                //find current store default language
                if (detectedLanguage == null)
                {
                    try
                    {
                        var currentStore = _storeContext.CurrentStore;
                        var allStoreLanguages = _languageService.GetAllLanguages(storeId: _storeContext.CurrentStore?.Id ?? 0);
                        var currentStoreLanguageId = _storeContext.CurrentStore?.DefaultLanguageId ?? 0;
                        detectedLanguage = allStoreLanguages.FirstOrDefault(language => language.Id == currentStoreLanguageId);
                        if (detectedLanguage == null) throw new PMSException("Language not detected from current store");
                    }
                    catch
                    {
                        detectedLanguage = _languageService.GetAllLanguages().FirstOrDefault();
                    }
                }
                
                //cache the found language
                _cachedLanguage = detectedLanguage;

                return _cachedLanguage;
            }
            set
            {
                _cachedLanguage = value ?? null;
            }
        }

        /// <summary>
        /// Gets or sets value indicating whether we're in admin area
        /// </summary>
        public virtual bool IsAdminArea { get; set; }

        /// <summary>
        /// Gets or sets value indicating whether we're in Guests area
        /// </summary>
        public virtual bool IsGuestsArea { get; set; }

        #endregion
    }
}
